package com.yunhe.transfer.websocket;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.yunhe.common.model.authority.Station;
import com.yunhe.transfer.client.AuthorityClient;
import com.yunhe.transfer.client.ControlClient;
import com.yunhe.common.constant.CacheConsts;
import com.yunhe.common.constant.ControlConsts;
import com.yunhe.common.model.ResultObject;
import com.yunhe.common.model.authority.StationDTO;
import com.yunhe.common.model.model.RunStrategyDistributeDTO;
import com.yunhe.common.util.RedisClient;
import com.yunhe.transfer.util.WebsocketUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.net.URI;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Component
@SuppressWarnings("ALL")
public class RunStrategyWebsocketHandler extends TextWebSocketHandler {

    private static final Map<String, WebSocketSession> stationSessionMap = new ConcurrentHashMap<>();

    private static final Map<WebSocketSession, String> sessionStationMap = new ConcurrentHashMap<>();

    private static final Map<String, String> tokenMap = new ConcurrentHashMap<>();

    private static final ObjectMapper mapper = new ObjectMapper();

    private static final Logger logger = LoggerFactory.getLogger(RunStrategyWebsocketHandler.class);

    @Autowired
    private RedisClient redisClient;
    @Autowired
    private WebsocketUtil websocketUtil;
    @Autowired
    private AuthorityClient authorityClient;
    @Autowired
    private ControlClient controlClient;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        URI sessionUri = session.getUri();
        if (sessionUri != null) {
            String stationCode = websocketUtil.getStationCode(sessionUri.getQuery());
            if (stationCode != null) {
                if (stationSessionMap.containsKey(stationCode)) {
                    try {
                        stationSessionMap.remove(stationCode).close();
                    } catch (Exception e) {
                        logger.error("关闭电站");
                    }
                }
                stationSessionMap.put(stationCode, session);
                sessionStationMap.put(session, stationCode);
            }
        }
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        URI sessionUri = session.getUri();
        if (sessionUri != null) {
            String stationCode = sessionStationMap.get(session);
            if (stationCode != null) {
                stationSessionMap.remove(stationCode);
                sessionStationMap.remove(session);
            }
        }
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) {
        //策略上传
        try {
            String payload = message.getPayload();
            Map<String, Object> dataMap = mapper.readValue(payload, Map.class);
            logger.info("接收到远程策略消息：{}", dataMap);
            if (dataMap != null) {
                Object tokenObj = dataMap.get("token");
                Object type = dataMap.get("type");
                if (type != null && (ControlConsts.RunStrategyType.Current.value().equals(type)
                        || ControlConsts.RunStrategyType.History.value().equals(type))) {
                    try {
                        String stationCode = sessionStationMap.get(session);
                        try {
                            Station station = authorityClient.getStationById(Long.parseLong(stationCode)).getResults();
                            if (station != null) {
                                dataMap.put("stationId", station.getId());
                            }
                        } catch (Exception e) {
                            logger.error("查询电站{}报错", stationCode);
                        }
                        logger.info("接收站端发送策略历史：{}", mapper.writeValueAsString(dataMap));
                        controlClient.saveRunStrategyHistory(Long.parseLong(stationCode), null, null, dataMap);
                    } catch (Exception e) {
                        logger.error("接收站端发送策略历史报错，", e);
                    }
                } else if (tokenObj != null) {
                    String token = tokenMap.get(tokenObj.toString());
                    if (token != null) {
                        synchronized (token) {
                            String key = CacheConsts.KEY.RUN_STRATEGY.value(token);
                            redisClient.setObject(key, dataMap, CacheConsts.getDefaultTime());
                            token.notifyAll();
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public ResultObject distributeRunStrategy(RunStrategyDistributeDTO runStrategyDistributeDTO) throws Exception {
        String stationCode = String.valueOf(runStrategyDistributeDTO.getStationCode());
        String token = UUID.randomUUID().toString();
        WebSocketSession session = stationSessionMap.get(stationCode);
        if (session != null) {
            synchronized (token) {
                tokenMap.put(token, token);
                runStrategyDistributeDTO.setToken(token);
                logger.info("下发策略：{}", mapper.writeValueAsString(runStrategyDistributeDTO));
                session.sendMessage(new TextMessage(mapper.writeValueAsString(runStrategyDistributeDTO)));
                token.wait(5000);
                tokenMap.remove(token);
            }
        } else {
            logger.error("电站{}没有连接", stationCode);
            return new ResultObject(9, "电站" + stationCode + "没有连接");
        }
        String key = CacheConsts.KEY.RUN_STRATEGY.value(token);
        Map<String, Object> dataMap = redisClient.getObject(key, Map.class);
        if (dataMap != null) {
            if ("0".equals(dataMap.get("errorCode").toString())) {
                logger.info("下发策略成功");
                return new ResultObject<>(0, null);
            } else {
                logger.error("下发策略失败：{}", dataMap);
                return new ResultObject<>(Integer.parseInt(dataMap.get("errorCode").toString()), dataMap.get("errorMsg").toString());
            }
        } else {
            logger.warn("下发策略没有返回结果");
            return new ResultObject<>(99, "下发策略失败");
        }
    }

    public ResultObject getLocalRunStrategy(RunStrategyDistributeDTO runStrategyDistributeDTO) throws Exception {
        Long stationCode = runStrategyDistributeDTO.getStationCode();
        String token = UUID.randomUUID().toString();
        tokenMap.put(token, token);
        WebSocketSession session = stationSessionMap.get(String.valueOf(stationCode));
        if (session != null) {
            synchronized (token) {
                runStrategyDistributeDTO.setToken(token);
                session.sendMessage(new TextMessage(mapper.writeValueAsString(runStrategyDistributeDTO)));
                token.wait(5000);
                tokenMap.remove(token);
            }
        } else {
            logger.error("电站{}没有连接", stationCode);
            return new ResultObject(9, "电站" + stationCode + "没有连接");
        }
        String key = CacheConsts.KEY.RUN_STRATEGY.value(token);
        Map<String, Object> dataMap = redisClient.getObject(key, Map.class);
        if (dataMap != null) {
            if ("0".equals(dataMap.get("errorCode").toString())) {
                logger.info("获取本地策略成功");
                return new ResultObject<>(0, null);
            } else {
                logger.error("获取本地策略失败：{}", dataMap);
                return new ResultObject<>(Integer.parseInt(dataMap.get("errorCode").toString()), dataMap.get("errorMsg").toString());
            }
        } else {
            logger.warn("获取本地策略没有返回结果");
            return new ResultObject<>(99, "获取本地策略失败");
        }
    }
}